﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using BrainTechLLC;
using BrainTechLLC.ThreadSafeObjects;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;

namespace BrainTechLLC.Silverlight.InputControls
{
    public static class FocusHandler
    {
        public static bool InTextField;

        public static FocusWatcher AttachFocusWatcher(this TextBox txt)
        {
            FocusWatcher f = new FocusWatcher(txt);
            return f;
        }
    }

    public class FocusWatcher
    {
        public FocusWatcher(TextBox txt)
        {
            txt.GotFocus += new RoutedEventHandler(txt_GotFocus);
            txt.LostFocus += new RoutedEventHandler(txt_LostFocus);
        }

        void txt_LostFocus(object sender, RoutedEventArgs e)
        {
            FieldLostFocus();
        }

        void txt_GotFocus(object sender, RoutedEventArgs e)
        {
            FieldGotFocus();
        }

        public void FieldGotFocus()
        {
            FocusHandler.InTextField = true;
        }

        public void FieldLostFocus()
        {
            FocusHandler.InTextField = false;
        }
    }

    public static class InputControlConstants
    {
        public static SolidColorBrush WhiteBrush = new SolidColorBrush(Colors.White);
        public static SolidColorBrush LightGrayBrush = new SolidColorBrush(Color.FromArgb(255, 220, 220, 220));
        public static SolidColorBrush HighlightFocusBrush = new SolidColorBrush(Color.FromArgb(255, 220, 220, 240));
        public static SolidColorBrush BrightYellowWarning = new SolidColorBrush(Color.FromArgb(255, 255, 255, 100));
        public static SolidColorBrush GreenValid = new SolidColorBrush(Color.FromArgb(255, 210, 255, 210));
        public static SolidColorBrush RedError = new SolidColorBrush(Color.FromArgb(255, 255, 210, 210));
        public static SolidColorBrush LightYellowWarning = new SolidColorBrush(Color.FromArgb(255, 255, 255, 210));
        public static SolidColorBrush FunkyColor = new SolidColorBrush(Color.FromArgb(255, 0, 100, 100));
    }
    
    public enum DefaultValue
    {
        None = 0,
        True = 1,
        False = 2
    }

    public static class RegExFactory
    {
        /// <summary>
        /// Given a ElementFormat (PHONE, EMAIL, etc), returns a regular expression that can provide input validation
        /// </summary>
        /// <param name="elementFormat"></param>
        /// <returns></returns>
        public static string CreateRegEx(TextFormat format)
        {
            switch (format)
            {
                case TextFormat.PHONE:
                    return CreateRegEx("Phone");

                case TextFormat.EMAIL:
                    return CreateRegEx("Email");

                case TextFormat.ZIP:
                    return CreateRegEx("Zip");

                case TextFormat.ZIP4:
                    return CreateRegEx("Zip4");

                case TextFormat.YYYY:
                    return CreateRegEx("YYYY");

                case TextFormat.NONE:
                default:
                    return string.Empty;
            }
        }

        /// <summary>
        /// Given a friendly name, returns a regular expression that can 
        /// provide input validation (example friendly names: Zipcode, 
        /// Zip, Number, MoneyAmount, MMDDYYYY, Phone, Email)
        /// </summary>
        /// <param name="regExFriendlyName"></param>
        /// <returns></returns>
        public static string CreateRegEx(string regExFriendlyName)
        {
            if (regExFriendlyName.Equals("Zipcode", StringComparison.OrdinalIgnoreCase) ||
                regExFriendlyName.Equals("Zip4", StringComparison.OrdinalIgnoreCase))
            {
                return "^(\\d{5}-\\d{4}|\\d{5}|[A-Za-z]\\d[A-Za-z] \\d[A-Za-z]\\d|[A-Za-z]\\d[A-Za-z]\\d[A-Za-z]\\d)$";
            }
            else if (regExFriendlyName.Equals("ShortZipcode", StringComparison.OrdinalIgnoreCase) ||
                     regExFriendlyName.Equals("Zip", StringComparison.OrdinalIgnoreCase))
            {
                return "^\\d{5}$";
            }
            else if (regExFriendlyName.Equals("Number", StringComparison.OrdinalIgnoreCase))
            {
                return "^\\d*$";
            }
            else if (regExFriendlyName.Equals("MoneyAmount", StringComparison.OrdinalIgnoreCase))
            {
                return "^(\\d{1,3}(\\,\\d{3})*|(\\d+))(\\.\\d{2})?$";
            }
            else if (regExFriendlyName.Equals("MMDDYYYY_1", StringComparison.OrdinalIgnoreCase))
            {
                // regular expressions suck
                return "^(?=\\d)(?:(?:(?:(?:(?:0?[13578]|1[02])(\\/|-|\\.)31)\\1|(?:(?:0?[1,3-9]|1[0-2])(\\/|-|\\.)(?:29|30)\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})|(?:0?2(\\/|-|\\.)29\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))|(?:(?:0?[1-9])|(?:1[0-2]))(\\/|-|\\.)(?:0?[1-9]|1\\d|2[0-8])\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2}))($|\\ (?=\\d)))?(((0?[1-9]|1[012])(:[0-5]\\d){0,2}(\\ [AP]M))|([01]\\d|2[0-3])(:[0-5]\\d){1,2})?$";
                //"^(((0[1-9]|[12]\\d|3[01])\\/(0[13578]|1[02])\\/((1[6-9]|[2-9]\\d)\\d{2}))|((0[1-9]|[12]\\d|30)\\/(0[13456789]|1[012])\\/((1[6-9]|[2-9]\\d)\\d{2}))|((0[1-9]|1\\d|2[0-8])\\/02\\/((1[6-9]|[2-9]\\d)\\d{2}))|(29\\/02\\/((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$";
                //"(^(((([1-9])|([0][1-9])|([1-2][0-9])|(30))\\-([A,a][P,p][R,r]|[J,j][U,u][N,n]|[S,s][E,e][P,p]|[N,n][O,o][V,v]))|((([1-9])|([0][1-9])|([1-2][0-9])|([3][0-1]))\\-([J,j][A,a][N,n]|[M,m][A,a][R,r]|[M,m][A,a][Y,y]|[J,j][U,u][L,l]|[A,a][U,u][G,g]|[O,o][C,c][T,t]|[D,d][E,e][C,c])))\\-[0-9]{4}$)|(^(([1-9])|([0][1-9])|([1][0-9])|([2][0-8]))\\-([F,f][E,e][B,b])\\-[0-9]{2}(([02468][1235679])|([13579][01345789]))$)|(^(([1-9])|([0][1-9])|([1][0-9])|([2][0-9]))\\-([F,f][E,e][B,b])\\-[0-9]{2}(([02468][048])|([13579][26]))$)";                
            }
            else if (regExFriendlyName.Equals("MMDDYYYY", StringComparison.OrdinalIgnoreCase))
            {
                return "^([1-9]|0[1-9]|1[012])[- /.]([1-9]|0[1-9]|[12][0-9]|3[01])[- /.][0-9]{4}$";
            }
            else if (regExFriendlyName.Equals("YYYY", StringComparison.OrdinalIgnoreCase))
            {
                return "^(0|([1-2]{1}[0123789]{1}[0-9]{2}))$";
            }
            else if (regExFriendlyName.Equals("Phone", StringComparison.OrdinalIgnoreCase))
            {
                return "^(\\d{10})$|^(((\\(\\d{3}\\) ?)|(\\d{3}-)){1}\\d{3}-\\d{4})$";
            }
            else if (regExFriendlyName.Equals("Email", StringComparison.OrdinalIgnoreCase))
            {
                return "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?";
            }
            else if (regExFriendlyName.Equals("NumberWithDecimal", StringComparison.OrdinalIgnoreCase))
            {
                return "^-?\\d{0,9}(\\.\\d{1,8})?$"; //"^\\d*\\.{0,1}\\d+$";				
            }

            return null;
        }
    }
    public static class TT
    {
        public static bool ReturnBoolean(this object o, DefaultValue defaultValue)
        {
            if (o == null)
                return (defaultValue == DefaultValue.True);

            return Convert.ToBoolean(o);
        }
        

        
        //public static bool IsValidZipCode(string p)
        //{
        //    return Regex.IsMatch(p, @"^\d{5}$");
        //}

        //public static bool IsValidCharOnly(string p)
        //{
        //    return Regex.IsMatch(p, @"^[a-zA-Z\s]*$");
        //}
                
        public static string BuildList<T>(this List<T> items, string delimiter, Func<T, string> func)
        {
            StringBuilder sb = new StringBuilder();
            for (int n = 0; n < items.Count; n++)
            {
                if (n > 0)
                    sb.Append(delimiter);

                sb.Append(func(items[n]));
            }
            return sb.ToString();
        }

    }
 
    public enum ReviewState2
    {
        NotSet = -1, None = 0, Valid = 1, Info = 2, Warning = 3, Invalid = 4
    }

    public enum ValidationType
    {
        NONE = 0,
        REQUIRED = 1,
        SOFT = 2,
    }

    public interface IInputControl2
    {
        event EventHandler<ObjectEventArgs<string>> ValueChanged;
        event EventHandler<ObjectEventArgsNonRef<ReviewState2, string>> NotifyReviewStateChange;

        Image ImageIcon { get; set; }
        string Value { get; set; }

        Control PrimaryInputControl { get; }
        ReviewState2 ControlReviewState { get; set; }
        object ParentSection { get; set; }
        void SetControlState(ReviewState2 state);

        Brush BackgroundForNone { get; set; }
        Brush BackgroundForInfo { get; set; }
        Brush BackgroundForInvalid { get; set; }
        Brush BackgroundForValid { get; set; }
        Brush BackgroundForNotSet { get; set; }
        Brush BackgroundForWarning { get; set; }
    }

    public static partial class ExtensionMethods
    {
        public static readonly Key[] AlwaysOkay = new Key[] { Key.Tab, Key.Shift, Key.Enter, Key.Right, Key.Left, Key.Delete, Key.Back };
        public static readonly Key[] AlwaysOkay2 = new Key[] { Key.Down, Key.Up, Key.Left, Key.Right, Key.Home, Key.End, Key.Tab, Key.F4, Key.Back, Key.Delete, Key.Enter, Key.Shift, Key.Ctrl, Key.CapsLock, Key.Alt };

        // DANG UNKNOWN KEY & SILVERLIGHT - . is Key.Unknown (you'd think it was Key.Decimal, but it's not...)
        public static readonly Key[] ValidNumberKeys = new Key[] { Key.Decimal, Key.D0, Key.D1, Key.D2, Key.D3, Key.D4, Key.D5, Key.D6, Key.D7, Key.D8, Key.D9,
                    Key.Unknown, Key.NumPad0, Key.NumPad1, Key.NumPad2, Key.NumPad3, Key.NumPad4, Key.NumPad5, Key.NumPad6, Key.NumPad7, Key.NumPad8, Key.NumPad9};

        public static readonly Key[] ValidIntegerKeys = new Key[] { Key.D0, Key.D1, Key.D2, Key.D3, Key.D4, Key.D5, Key.D6, Key.D7, Key.D8, Key.D9,
                                    Key.NumPad0, Key.NumPad1, Key.NumPad2, Key.NumPad3, Key.NumPad4, Key.NumPad5, Key.NumPad6, Key.NumPad7, Key.NumPad8, Key.NumPad9};

        public static bool IsKeystrokeAllowed(this DataType dataType, Key key, Key modifierKeys, bool shiftKeyDown)
        {
            if (key.IsIn(AlwaysOkay) || key.IsIn(AlwaysOkay2))
                return true;

            // Need a cleaner way to limit user input
            if ((dataType == DataType.CURRENCY || dataType == DataType.DOUBLE || dataType == DataType.PERCENTAGE) && (!key.IsIn(ValidNumberKeys)))
            {
                if (dataType != DataType.PERCENTAGE || (key != Key.D5))
                {
                    return false;
                }
            }
            else if (dataType == DataType.INTEGER && (shiftKeyDown || !key.IsIn(ValidIntegerKeys)))
            {
                return false;
            }

            return true;
        }

        public static bool IsIn<T>(this T item, params T[] setToCheck)
        {
            foreach (T check in setToCheck)
            {
                if (item.Equals(check))
                    return true;
            }

            return false;
        }

        public static string GetUnformattedText(this string originalText, DataType dataType, TextFormat format)
        {
            switch (dataType)
            {
                case DataType.DOUBLE:
                    return originalText.GetUnformattedDouble();

                case DataType.CURRENCY:
                    return originalText.GetUnformattedCurrency();

                case DataType.PERCENTAGE:
                    return originalText.Replace("%", "");

                case DataType.INTEGER:
                    return originalText.GetUnformattedInteger(format);

                case DataType.DATE:
                    return originalText.GetUnformattedDate();
            }

            return originalText;
        }

        public static bool ValidateValue(this DataType dataType, string value)
        {
            switch (dataType)
            {
                case DataType.CURRENCY:
                    return value.IsValidMoneyAmount(ReturnIfEmpty.True);

                case DataType.DATE:
                    return value.IsValidDate(true, ReturnIfEmpty.True);

                case DataType.DOUBLE:
                    return value.IsNumeric();

                case DataType.INTEGER:
                    return value.IsValidInteger(ReturnIfEmpty.True);

                case DataType.PERCENTAGE:
                    return value.IsValidPercentage(ReturnIfEmpty.True);
            }

            return true;
        }

        public static int CountLines(this string s)
        {
            int lines = 0;
            int n = 0;
            while (n >= 0)
            {
                if (n == 0)
                    n = -1;

                n++;
                int idx = s.IndexOf(Environment.NewLine, n);
                n = idx;
                lines++;
            }
            return lines;
        }

        public static string GetUnformattedDouble(this string originalText)
        {
            double d;

            originalText = originalText.Replace("$", "").Replace(",", "");

            d = CreateNumeric(originalText);

            if (d != double.NegativeInfinity)
                return d.ToString("N0").Replace(",", "");

            return originalText;
        }

        /// <summary>
        /// Returns a value formatted for output on the screen
        /// </summary>
        public static string GetScreenFormatted(this TextFormat format, DataType dataType, Type validatingType, string text)
        {
            if (validatingType == typeof(DateTime) || dataType == DataType.DATE)
            {
                DateTime dt;

                if (DateTime.TryParse(text, out dt))
                    return dt.ToString("MM/dd/yyyy");
            }
            else if (dataType == DataType.CURRENCY)
            {
                return "$" + text.GetUnformattedCurrency();
            }
            else if (dataType == DataType.PERCENTAGE)
            {
                return text.Replace("%", "").GetUnformattedDouble() + "%";
            }
            else
            {
                string textTemp = text;

                switch (format)
                {
                    case TextFormat.PHONE:
                        textTemp = text.CleanPhoneNumber().FormatPhoneNumber();
                        break;
                }

                return textTemp;
            }

            return text;
        }

        /// <summary>
        /// Returns a value suitable for a form data row vo value
        /// </summary>
        public static string GetUnformatted(this string text, TextFormat format, DataType dataType)
        {
            switch (format)
            {
                case TextFormat.PHONE:
                    return text.CleanPhoneNumber();
            }

            switch (dataType)
            {
                case DataType.CURRENCY:
                    return text.GetUnformattedCurrency();
                case DataType.PERCENTAGE:
                    return text.GetUnformattedText(dataType, format);
            }

            return text;
        }

        public static string GetUnformattedCurrency(this string originalText)
        {
            double d;
            originalText = originalText.Replace("$", "");

            d = CreateNumeric(originalText);

            if (d != double.NegativeInfinity)
                return d.ToString("N0");

            return originalText;
        }

        public static string GetUnformattedInteger(this string originalText, TextFormat format)
        {
            double d;

            originalText = originalText.Replace("$", "").Replace(",", "");

            d = CreateNumeric(originalText);

            if (d != double.NegativeInfinity && !originalText.Contains(".") && format != TextFormat.YYYY && format != TextFormat.ZIP)
                return d.ToString("N0").Replace(",", "");

            return originalText;
        }

        public static string GetUnformattedDate(this string originalText)
        {
            int temp;
            if (int.TryParse(originalText, out temp))
            {
                if (originalText.Length == 6 && originalText.Substring(4, 2) != "19" && originalText.Substring(4, 2) != "20")
                {
                    if (Convert.ToInt32(originalText.Substring(4, 2)) > 34)
                        return originalText.Substring(0, 2) + "/" +
                            originalText.Substring(2, 2) + "/19" +
                            originalText.Substring(4, 2);
                    else
                        return originalText.Substring(0, 2) + "/" +
                                originalText.Substring(2, 2) + "/20" +
                                originalText.Substring(4, 2);
                }
                else if (originalText.Length == 8)
                {
                    return originalText.Substring(0, 2) + "/" +
                            originalText.Substring(2, 2) + "/" +
                            originalText.Substring(4, 4);
                }
            }
            return originalText;
        }

        public static bool IsNumeric(this string value)
        {
            double d = CreateNumeric(value);
            return (d != double.NegativeInfinity);
        }

        public static double CreateNumeric(this string value)
        {
            double result;

            if (double.TryParse(
                value,
                NumberStyles.AllowCurrencySymbol | NumberStyles.AllowDecimalPoint |
                NumberStyles.AllowLeadingSign | NumberStyles.AllowLeadingWhite |
                NumberStyles.AllowThousands | NumberStyles.AllowTrailingWhite,
                null,
                out result))
            {
                return result;
            }

            if (value.EndsWith("."))
            {
                if (double.TryParse(
                value.Substring(0, value.Length - 1),
                NumberStyles.AllowCurrencySymbol | NumberStyles.AllowDecimalPoint |
                NumberStyles.AllowLeadingSign | NumberStyles.AllowLeadingWhite |
                NumberStyles.AllowThousands | NumberStyles.AllowTrailingWhite,
                null,
                out result))
                {
                    return result;
                }
            }

            return double.NegativeInfinity;
        }

        public static string FormatPhoneNumber(this string number)
        {
            if (number.Length != 10)
                return number;

            return string.Format("({0}) {1}-{2}",
                        number.Substring(0, 3),
                        number.Substring(3, 3),
                        number.Substring(6));
        }

        public static string CleanPhoneNumber(this string number)
        {
            return number.Replace("-", "").Replace("(", "").Replace(")", "").Replace(" ", "");
        }

        public static bool IsValidZipCode(string p)
        {
            return Regex.IsMatch(p, @"^\d{5}$");
        }

        public static bool IsValidCharOnly(string p)
        {
            return Regex.IsMatch(p, @"^[a-zA-Z\s]*$");
        }

        // TODO - move to FinalTrack.Common.Util
        public static bool IsValidMoneyAmount(this string input, ReturnIfEmpty returnIfEmpty)
        {
            if (string.IsNullOrEmpty(input))
                return (returnIfEmpty == ReturnIfEmpty.True);

            bool isNumber = Regex.IsMatch(input, CreateRegEx("MoneyAmount"));

            if (isNumber)
            {
                int n;

                if (!int.TryParse(input.Replace("$", "").Replace(",", ""), out n))
                    isNumber = false;
            }

            return isNumber;
        }

        public static bool IsValidDate(this string input, bool futureDatesAreValid, ReturnIfEmpty returnIfEmpty)
        {
            if (string.IsNullOrEmpty(input))
                return (returnIfEmpty == ReturnIfEmpty.True);

            bool isDate = Regex.IsMatch(input, CreateRegEx("MMDDYYYY_1"));

            if (isDate)
            {
                DateTime dt;

                if (!DateTime.TryParse(input, out dt))
                    isDate = false;
                else if (dt < new DateTime(1801, 1, 1))
                    isDate = false;
                else if (!futureDatesAreValid && dt > DateTime.Now)
                    isDate = false;
            }

            return isDate;
        }

        public static bool IsValidInteger(this string input, ReturnIfEmpty returnIfEmpty)
        {
            if (string.IsNullOrEmpty(input))
                return (returnIfEmpty == ReturnIfEmpty.True);

            bool isNumber = Regex.IsMatch(input, CreateRegEx("Number"));

            if (isNumber)
            {
                int n;

                if (!int.TryParse(input, out n))
                    isNumber = false;
            }

            return isNumber;
        }

        public static string CreateRegEx(string regExFriendlyName)
        {
            if (regExFriendlyName.Equals("Zipcode", StringComparison.OrdinalIgnoreCase) ||
                regExFriendlyName.Equals("Zip4", StringComparison.OrdinalIgnoreCase))
            {
                return "^(\\d{5}-\\d{4}|\\d{5}|[A-Za-z]\\d[A-Za-z] \\d[A-Za-z]\\d|[A-Za-z]\\d[A-Za-z]\\d[A-Za-z]\\d)$";
            }
            else if (regExFriendlyName.Equals("ShortZipcode", StringComparison.OrdinalIgnoreCase) ||
                     regExFriendlyName.Equals("Zip", StringComparison.OrdinalIgnoreCase))
            {
                return "^\\d{5}$";
            }
            else if (regExFriendlyName.Equals("Number", StringComparison.OrdinalIgnoreCase))
            {
                return "^\\d*$";
            }
            else if (regExFriendlyName.Equals("MoneyAmount", StringComparison.OrdinalIgnoreCase))
            {
                return "^(\\d{1,3}(\\,\\d{3})*|(\\d+))(\\.\\d{2})?$";
            }
            else if (regExFriendlyName.Equals("MMDDYYYY_1", StringComparison.OrdinalIgnoreCase))
            {
                // regular expressions suck
                return "^(?=\\d)(?:(?:(?:(?:(?:0?[13578]|1[02])(\\/|-|\\.)31)\\1|(?:(?:0?[1,3-9]|1[0-2])(\\/|-|\\.)(?:29|30)\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})|(?:0?2(\\/|-|\\.)29\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))|(?:(?:0?[1-9])|(?:1[0-2]))(\\/|-|\\.)(?:0?[1-9]|1\\d|2[0-8])\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2}))($|\\ (?=\\d)))?(((0?[1-9]|1[012])(:[0-5]\\d){0,2}(\\ [AP]M))|([01]\\d|2[0-3])(:[0-5]\\d){1,2})?$";
            }
            else if (regExFriendlyName.Equals("MMDDYYYY", StringComparison.OrdinalIgnoreCase))
            {
                return "^([1-9]|0[1-9]|1[012])[- /.]([1-9]|0[1-9]|[12][0-9]|3[01])[- /.][0-9]{4}$";
            }
            else if (regExFriendlyName.Equals("YYYY", StringComparison.OrdinalIgnoreCase))
            {
                return "^(0|([1-2]{1}[0123789]{1}[0-9]{2}))$";
            }
            else if (regExFriendlyName.Equals("Phone", StringComparison.OrdinalIgnoreCase))
            {
                return "^(\\d{10})$|^(((\\(\\d{3}\\) ?)|(\\d{3}-)){1}\\d{3}-\\d{4})$";
            }
            else if (regExFriendlyName.Equals("Email", StringComparison.OrdinalIgnoreCase))
            {
                return "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?";
            }
            else if (regExFriendlyName.Equals("NumberWithDecimal", StringComparison.OrdinalIgnoreCase))
            {
                return "^-?\\d{0,9}(\\.\\d{1,8})?$";
            }

            return null;
        }

        public static bool IsValidYear(this string input, ReturnIfEmpty returnIfEmpty)
        {
            if (string.IsNullOrEmpty(input))
                return (returnIfEmpty == ReturnIfEmpty.True);

            return Regex.IsMatch(input, CreateRegEx("Number"));
        }

        public static bool IsValidPercentage(this string input, ReturnIfEmpty returnIfEmpty)
        {
            if (string.IsNullOrEmpty(input))
                return (returnIfEmpty == ReturnIfEmpty.True);

            return input.IsNumeric() || (input.Substring(0, input.Length - 1).IsNumeric() && input.Substring(input.Length - 2, 1) == "%");
        }

        /// <summary>
        /// Extension routine for object comparison (won't barf if o1 / o2 is null)
        /// </summary>
        public static bool AreEqual(this object o1, object o2)
        {
            if (o1 == null && o2 == null) return true;
            if ((o1 == null && o2 != null) || (o1 != null && o2 == null)) return false;
            return o1.Equals(o2);
        }
    }

    public enum ReturnIfEmpty
    {
        False = 0,
        True = 1
    }

    public enum DataType
    {
        STRING = 0,
        TEXT,
        INTEGER,
        DOUBLE,
        CURRENCY,
        DATE,
        BOOLEAN,
        PERCENTAGE
    }

    public enum TextFormat
    {
        NONE = 0,
        PHONE,
        EMAIL,
        ZIP,
        ZIP4,
        YYYY
    }
}
